﻿/****************************************************************
* MACHINE: DESKTOP-I6HF1HG
* NAME: gjp
* CREATEDATE: 2019/3/4 11:05:34
* DESC: <DESCRIPTION>
* **************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;

namespace LEE.Tools.Data
{
    /// <summary>
    /// Summary description for DataTableHelper
    /// </summary>
    public static class DataTableHelper
    {
        /// <summary>
        /// 把DataTable抓换为实体列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="table"></param>
        /// <returns></returns>
        public static List<T> ToEntityList<T>(this DataTable table) where T : new()
        {
            var list = new List<T>();
            var entityProperties = typeof(T).GetProperties();

            foreach (DataRow row in table.Rows)
            {
                var entity = new T();
                foreach (var item in entityProperties)
                {
                    var display = item.GetCustomAttribute<DisplayNameAttribute>();
                    var name = display == null ? item.Name : display.DisplayName;
                    if (!row.Table.Columns.Contains(name)) continue;
                    if (DBNull.Value != row[name])
                    {
                        item.SetValue(entity, ChangeType(row[name], item.PropertyType), null);
                    }
                }
                list.Add(entity);
            }

            return list;
        }

        /// <summary>
        /// 把列表转换为DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(this List<T> list) where T : class
        {
            var result = new DataTable();
            var entityProperties = typeof(T).GetProperties();
            foreach (var item in entityProperties)
            {
                var display = item.GetCustomAttribute<DisplayNameAttribute>();
                var name = display == null ? item.Name : display.DisplayName;
                result.Columns.Add(name);
            }
            list.ForEach(item =>
            {
                result.Rows.Add(entityProperties.Select(p => ChangeType(p.GetValue(item), typeof(string))).ToArray());
            });
            return result;
        }

        /// <summary>
        /// 类型转换（包含Nullable<>和非Nullable<>转换）
        /// </summary>
        /// <param name="value"></param>
        /// <param name="conversionType"></param>
        /// <returns></returns>
        private static object ChangeType(object value, Type conversionType)
        {
            object result;

            if (conversionType == null)
            {
                throw new ArgumentNullException(nameof(conversionType));
            }

            // If it's not a nullable type, just pass through the parameters to Convert.ChangeType

            if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                if (value == null)
                {
                    return null;
                }

                // It's a nullable type, and not null, so that means it can be converted to its underlying type,
                // so overwrite the passed-in conversion type with this underlying type
                var nullableConverter = new System.ComponentModel.NullableConverter(conversionType);

                conversionType = nullableConverter.UnderlyingType;
            }

            try
            {
                if (conversionType == typeof(DateTime))
                {
                    result = value != null ? Convert.ToDateTime(value) : DateTime.MinValue;
                }
                else
                {
                    // Now that we've guaranteed conversionType is something Convert.ChangeType can handle (i.e. not a
                    // nullable type), pass the call on to Convert.ChangeType
                    result = Convert.ChangeType(value, conversionType);
                }
            }
            catch
            {
                result = null;
            }
            return result;
        }
    }
}
